//
// Created by Administrator on 2018/5/9 0009.
//
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <android/bitmap.h>
#include <android/log.h>

#include <functional>

#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,"Terry",__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,"Terry",__VA_ARGS__)





jobject mat_to_bitmap(JNIEnv *env, int width,int height, jobject bitmap_config, const std::function<void(void*,cv::Mat)>& processor) {
    jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap");
    jmethodID mid = env->GetStaticMethodID(java_bitmap_class,
                                           "createBitmap",
                                           "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");

    jobject bitmap = env->CallStaticObjectMethod(java_bitmap_class,
                                                 mid, width, height,
                                                 bitmap_config);
    AndroidBitmapInfo info;
    void *pixels = 0;

    try {
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);
        if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
            //processor(pixels);
        }

        AndroidBitmap_unlockPixels(env, bitmap);
        return bitmap;
    } catch (cv::Exception e) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("org/opencv/core/CvException");
        if (!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
        return bitmap;
    } catch (...) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
        return bitmap;
    }
}


extern "C"
JNIEXPORT jobject

JNICALL
Java_com_cloudream_algorithm_YUVImageTools_getBGRImage(
        JNIEnv *env,
        jobject obj,
        jbyteArray arr,
        jint width,
        jint height,
        jobject config) {



    jsize len = env->GetArrayLength(arr);
    jbyte *jbarray = (jbyte *)malloc(len * sizeof(jbyte));
    env->GetByteArrayRegion(arr,0,len,jbarray);

    jint * argb_ = (jint *)malloc(height * width * sizeof(jint));

    cv::Mat yuv(height*3/2,width,CV_8UC1,jbarray);
    // Mat 与 图片长度是反的
    cv::Mat bgra(height,width,CV_8UC4,argb_);

    cv::cvtColor(yuv,bgra,CV_YUV420sp2BGRA);



    jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap");
    jmethodID mid = env->GetStaticMethodID(java_bitmap_class,
                                           "createBitmap",
                                           "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");

    jobject bitmap = env->CallStaticObjectMethod(java_bitmap_class,
                                                 mid, width, height,
                                                 config);

    AndroidBitmapInfo info;
    void *pixels = 0;

    try {
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);

        LOGI("info.width = %d,info.height = %d",info.width,info.height);

        cv::Mat dst(height,width,CV_8UC4,pixels);
        cv::cvtColor(bgra,dst,CV_BGRA2RGBA);

        AndroidBitmap_unlockPixels(env, bitmap);
    } catch (cv::Exception e) {
        AndroidBitmap_unlockPixels(env, bitmap);
        LOGI("cv error");
        jclass je = env->FindClass("org/opencv/core/CvException");
        if (!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
    } catch (...) {
        LOGI("error");
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");

    }


    free(jbarray);
    free(argb_);


    if(arr != NULL){
        env->DeleteLocalRef(arr);
    }
    if(config != NULL){
        env->DeleteLocalRef(config);
    }

    return bitmap;
}




extern "C"
JNIEXPORT jint

JNICALL
Java_com_cloudream_algorithm_YUVImageTools_getARGBImageWithRotation(
        JNIEnv *env,
        jobject obj,
        jbyteArray arr,
        jobject bitmap) {



    AndroidBitmapInfo info;
    void *pixels = 0;


    if(AndroidBitmap_getInfo(env, bitmap, &info) < 0){
        LOGE("AndroidBitmap_getInfo fail ");
        return  -1;
    }

    int lockResult = AndroidBitmap_lockPixels(env, bitmap, &pixels);
    if(lockResult < 0){
        LOGE("AndroidBitmap_lockPixels fail ,code = %d",lockResult);
        return -1;
    }

    int preViewWidth = info.height;
    int preViewHeight = info.width;

    jsize len = env->GetArrayLength(arr);
    jbyte *jbarray = (jbyte *)malloc(len * sizeof(jbyte));
    env->GetByteArrayRegion(arr,0,len,jbarray);

    jint * intarray = (jint *)malloc(preViewHeight * preViewWidth * sizeof(jint));
    //jint * argb_ = (jint *)malloc(height * width * sizeof(jint));

    cv::Mat yuv(preViewHeight*3/2,preViewWidth,CV_8UC1,jbarray);
    cv::Mat bgra(preViewHeight,preViewWidth,CV_8UC4,intarray);

    cv::cvtColor(yuv,bgra,CV_YUV420sp2BGRA);

// 计算旋转中心点
    jint minWidth = preViewWidth < preViewHeight?preViewWidth:preViewHeight;
    cv::Point2f pCenter(minWidth/2,minWidth/2);

    LOGI("pCenter = %lf * %lf ",pCenter.x,pCenter.y);

    // 得到旋转矩阵
    cv::Mat rotationMatrix = cv::getRotationMatrix2D(pCenter,270,1);


    try{


        LOGI("info.width = %d,info.height = %d",info.width,info.height);


        cv::Mat dst(info.width,info.height,CV_8UC4);
        cv::cvtColor(bgra,dst,CV_BGRA2RGBA);

        cv::Mat argb(info.height,info.width,CV_8UC4,pixels);
        cv::warpAffine(dst,argb,rotationMatrix,argb.size());


        AndroidBitmap_unlockPixels(env, bitmap);

    } catch (...) {
        LOGI("error");
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
    }


    free(jbarray);
    free(intarray);
    //free(argb_);

    if(arr != NULL){
        env->DeleteLocalRef(arr);
    }
    return 1;
}



extern "C"
JNIEXPORT jobject
JNICALL Java_com_cloudream_algorithm_YUVImageTools_getBGRImageWithRotation2(
        JNIEnv *env,
        jobject obj,
        jbyteArray arr,
        jobject bitmap,
        jint width,
        jint height

){


    jsize len = env->GetArrayLength(arr);
    jbyte *jbarray = (jbyte *)malloc(len * sizeof(jbyte));
    env->GetByteArrayRegion(arr,0,len,jbarray);

    jint * intarray = (jint *)malloc(height * width * sizeof(jint));
    //jint * argb_ = (jint *)malloc(height * width * sizeof(jint));

    cv::Mat yuv(height*3/2,width,CV_8UC1,jbarray);
    cv::Mat bgra(height,width,CV_8UC4,intarray);

    cv::cvtColor(yuv,bgra,CV_YUV420sp2BGRA);

// 计算旋转中心点
    jint minWidth = width < height?width:height;
    cv::Point2f pCenter(minWidth/2,minWidth/2);

    LOGI("pCenter = %lf * %lf ",pCenter.x,pCenter.y);

    // 得到旋转矩阵
    cv::Mat rotationMatrix = cv::getRotationMatrix2D(pCenter,270,1);


    AndroidBitmapInfo info;
    void *pixels = 0;

    try {
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);

        LOGI("info.width = %d,info.height = %d",info.width,info.height);


        cv::Mat dst(info.width,info.height,CV_8UC4);
        cv::cvtColor(bgra,dst,CV_BGRA2RGBA);

        cv::Mat argb(info.height,info.width,CV_8UC4,pixels);
        cv::warpAffine(dst,argb,rotationMatrix,argb.size());


        AndroidBitmap_unlockPixels(env, bitmap);
    } catch (cv::Exception e) {
        AndroidBitmap_unlockPixels(env, bitmap);
        LOGI("cv error");
        jclass je = env->FindClass("org/opencv/core/CvException");
        if (!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
    } catch (...) {
        LOGI("error");
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");

    }



    free(jbarray);
    free(intarray);
    //free(argb_);



    return bitmap;


}


extern "C"
JNIEXPORT jobject

JNICALL
Java_com_cloudream_algorithm_YUVImageTools_getBGRImageWithRotation(
        JNIEnv *env,
        jobject obj,
        jbyteArray arr,
        jint width,
        jint height,
        jobject config) {



    jsize len = env->GetArrayLength(arr);
    jbyte *jbarray = (jbyte *)malloc(len * sizeof(jbyte));
    env->GetByteArrayRegion(arr,0,len,jbarray);

    jint * intarray = (jint *)malloc(height * width * sizeof(jint));
    //jint * argb_ = (jint *)malloc(height * width * sizeof(jint));

    cv::Mat yuv(height*3/2,width,CV_8UC1,jbarray);
    cv::Mat bgra(height,width,CV_8UC4,intarray);

    cv::cvtColor(yuv,bgra,CV_YUV420sp2BGRA);

// 计算旋转中心点
    jint minWidth = width < height?width:height;
    cv::Point2f pCenter(minWidth/2,minWidth/2);

    LOGI("pCenter = %lf * %lf ",pCenter.x,pCenter.y);

    // 得到旋转矩阵
    cv::Mat rotationMatrix = cv::getRotationMatrix2D(pCenter,270,1);


    jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap");
    jmethodID mid = env->GetStaticMethodID(java_bitmap_class,
                                           "createBitmap",
                                           "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");

    jobject bitmap = env->CallStaticObjectMethod(java_bitmap_class,
                                                 mid, height, width,
                                                 config);

    AndroidBitmapInfo info;
    void *pixels = 0;

    try {
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);

        LOGI("info.width = %d,info.height = %d",info.width,info.height);


        cv::Mat dst(info.width,info.height,CV_8UC4);
        cv::cvtColor(bgra,dst,CV_BGRA2RGBA);

        cv::Mat argb(info.height,info.width,CV_8UC4,pixels);
        cv::warpAffine(dst,argb,rotationMatrix,argb.size());


        AndroidBitmap_unlockPixels(env, bitmap);
    } catch (cv::Exception e) {
        AndroidBitmap_unlockPixels(env, bitmap);
        LOGI("cv error");
        jclass je = env->FindClass("org/opencv/core/CvException");
        if (!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
    } catch (...) {
        LOGI("error");
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");

    }



    free(jbarray);
    free(intarray);
    //free(argb_);


    if(arr != NULL){
        env->DeleteLocalRef(arr);
    }
    if(config != NULL){
        env->DeleteLocalRef(config);
    }

    return bitmap;
}

extern "C"
JNIEXPORT jbyteArray

JNICALL
Java_com_cloudream_algorithm_YUVImageTools_yuvReduceWidth(
        JNIEnv *env,
        jobject obj,
        jbyteArray yuvIn,
        jint width,
        jint height,
        jint start
){

    int newHeight = height*3/2;
    jsize yuv_in_len = env->GetArrayLength(yuvIn);
    jbyte * p_yunIn = (jbyte *) malloc(yuv_in_len * sizeof(jbyte));
    jsize yuv_out_len = (newHeight*(width - start));
    jbyte * p_yunout = (jbyte *) malloc(yuv_out_len * sizeof(jbyte));

    env->GetByteArrayRegion(yuvIn,0,yuv_in_len,p_yunIn);

    cv::Mat yuv(newHeight,width,CV_8UC1,p_yunIn);

    cv::Mat right(newHeight,width - start,CV_8UC1,p_yunout);

    yuv(cv::Rect(start,0,width-start,newHeight)).convertTo(right,right.type(),1,0);

    jbyteArray  yuvOut = env->NewByteArray(yuv_out_len) ;
    env->SetByteArrayRegion(yuvOut,0,yuv_out_len,p_yunout);

    free(p_yunIn);
    free(p_yunout);

    return yuvOut;

}







extern "C"
JNIEXPORT jobject

JNICALL
Java_com_cloudream_algorithm_YUVImageTools_getRotationBitmap(
        JNIEnv *env,
        jobject obj,
        jbyteArray arr,
        jdouble rotation,
        jint width,
        jint height,
        jobject config
){

    jsize len = env->GetArrayLength(arr);
    jbyte *jarray = (jbyte *)malloc(len * sizeof(jbyte) *4 );
    env->GetByteArrayRegion(arr,0,len,jarray);

    cv::Mat argbMat(height,width,CV_8UC4,jarray);

    // 计算旋转中心点
    jint minWidth = width < height?width:height;
    cv::Point2f pCenter(minWidth/2,minWidth/2);

    // 得到旋转矩阵
    cv::Mat rotationMatrix = cv::getRotationMatrix2D(pCenter,rotation,1);



    jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap");
    jmethodID mid = env->GetStaticMethodID(java_bitmap_class,
                                           "createBitmap",
                                           "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");



    LOGI("argbMat.size().width = %d,height = %d",argbMat.size().width,argbMat.size().height);

    jobject bitmap = env->CallStaticObjectMethod(java_bitmap_class,
                                                 mid, argbMat.size().height, argbMat.size().width,
                                                 config);
    AndroidBitmapInfo info;
    void *pixels = 0;

    try {
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);

        LOGI("info.width = %d,info.height = %d",info.width,info.height);

        cv::Mat dst(info.height,info.width,CV_8UC4,pixels);
        cv::warpAffine(argbMat,dst,rotationMatrix,dst.size());



        AndroidBitmap_unlockPixels(env, bitmap);

    } catch (cv::Exception e) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("org/opencv/core/CvException");
        if (!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
    } catch (...) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
    }
    free(jarray);
    if(arr != NULL){
        env->DeleteLocalRef(arr);
    }
    if(config != NULL){
        env->DeleteLocalRef(config);
    }

    return bitmap;

}